#pragma once

#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef WIN32
#include <netinet/in.h>
#include <sys/socket.h>
#endif

#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/thread.h>


#include <log4cplus/logger.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/configurator.h>

#define LOG4CPLUS_INIT(param)	log4cplus::PropertyConfigurator::doConfigure(param)
#define LOG4CPLUS_DECLARE(logger)			extern log4cplus::Logger logger
#define LOG4CPLUS_IMPLEMENT(logger, name)	log4cplus::Logger logger = log4cplus::Logger::getInstance(name)
#define LOG4CPLUS_CLASS_DECLARE(logger)						static log4cplus::Logger logger
#define LOG4CPLUS_CLASS_IMPLEMENT(classname, logger, name)	log4cplus::Logger classname::logger = log4cplus::Logger::getInstance(name)

#define LOG4CPLUS_THIS_TRACE(a,b)	LOG4CPLUS_TRACE(a, '[' << this << ']' << b )
#define LOG4CPLUS_THIS_DEBUG(a,b)	LOG4CPLUS_DEBUG(a, '[' << this << ']' << b )
#define LOG4CPLUS_THIS_INFO(a,b)	LOG4CPLUS_INFO(a, '[' << this << ']' << b )
#define LOG4CPLUS_THIS_WARN(a,b)	LOG4CPLUS_WARN(a, '[' << this << ']' << b )
#define LOG4CPLUS_THIS_ERROR(a,b)	LOG4CPLUS_ERROR(a, '[' << this << ']' << b )
#define LOG4CPLUS_THIS_FATAL(a,b)	LOG4CPLUS_FATAL(a, '[' << this << ']' << b )

LOG4CPLUS_DECLARE(g_logger);

#include <string>
#include <sstream>
#include <assert.h>
#include <algorithm>

typedef int int32;
typedef unsigned int uint32;

typedef short int16;
typedef unsigned short uint16;

typedef unsigned char uint8;
typedef char int8;
typedef unsigned char byte;

#ifdef WIN32
typedef unsigned __int64 uint64;
typedef signed __int64 int64;
#else
typedef unsigned long long uint64;
typedef long long int64;
#endif

#ifndef WIN32
#define INVALID_SOCKET -1
#endif

#define IP_HEAD_LEN 20
#define UDP_HEAD_LEN 8
#define TCP_HEAD_LEN 20

#define MSS_SIZE 1500
#define MIN_SAMPLES 50
#define PROBE_TIME 0.5//second

#define PROBING_DELAY 100 //100ms


#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0]))))

inline void SetSockAddr(sockaddr_in &addr, uint32 ip, uint16 port)
{
	memset(&addr, 0, sizeof(addr));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = htonl(ip);
}

inline void GetSockAddr(const sockaddr_in &addr, uint32& ip, uint16& port)
{
	ip = ntohl(addr.sin_addr.s_addr);
	port = ntohs(addr.sin_port);
}

#ifndef WIN32

#define sleep_ms(t) usleep(t*1000)

#include <sys/time.h>
inline uint32 GetTickCount()
{
	struct timeval timev;
	uint32 et;
	gettimeofday(&timev, NULL);

	et = timev.tv_sec * 1000 + timev.tv_usec/ 1000;//ms

	return et;
}
#else

#define sleep_ms(t) Sleep(t)

#endif

#ifndef WIN32

/* True iff e is an error that means a read/write operation can be retried. */
#define UTDUTIL_ERR_RW_RETRIABLE(e)				\
	((e) == EINTR || (e) == EAGAIN)
/* True iff e is an error that means an connect can be retried. */
#define UTDUTIL_ERR_CONNECT_RETRIABLE(e)			\
	((e) == EINTR || (e) == EINPROGRESS)
/* True iff e is an error that means a accept can be retried. */
#define UTDUTIL_ERR_ACCEPT_RETRIABLE(e)			\
	((e) == EINTR || (e) == EAGAIN || (e) == ECONNABORTED)

/* True iff e is an error that means the connection was refused */
#define UTDUTIL_ERR_CONNECT_REFUSED(e)					\
	((e) == ECONNREFUSED)

#else

#define UTDUTIL_ERR_RW_RETRIABLE(e)					\
	((e) == WSAEWOULDBLOCK ||					\
	(e) == WSAEINTR)

#define UTDUTIL_ERR_CONNECT_RETRIABLE(e)					\
	((e) == WSAEWOULDBLOCK ||					\
	(e) == WSAEINTR ||						\
	(e) == WSAEINPROGRESS ||					\
	(e) == WSAEINVAL)

#define UTDUTIL_ERR_ACCEPT_RETRIABLE(e)			\
	UTDUTIL_ERR_RW_RETRIABLE(e)

#define UTDUTIL_ERR_CONNECT_REFUSED(e)					\
	((e) == WSAECONNREFUSED)

#endif

#ifdef offsetof
#define ntdtil_offsetof(type, field) offsetof(type, field)
#else
#define ntdtil_offsetof(type, field) ((off_t)(&((type *)0)->field))
#endif

#define NTDTIL_UPCAST(ptr, type, field)				\
	((type *)(((char*)(ptr)) - ntdtil_offsetof(type, field)))


uint64 byte_atoi( const char *inString );

std::string byte_format(double inNum, char inFormat);

enum APP_MODE
{
	Mode_Client,
	Mode_Server,
};

static const int PORT = 9995;

struct NDTSettings{
	NDTSettings()
	{
		m_app_mode = Mode_Client;
		m_bw_limit = UINT_MAX;
		m_host = "127.0.0.1";
		m_port = PORT;
		m_bUdp = false;
		m_bRunOnce = false;
		m_bPacketPair = false;

		m_len = 0;
		m_mss = MSS_SIZE;
		m_samples = MIN_SAMPLES;
		m_probeTime = PROBE_TIME;
	}
	APP_MODE m_app_mode;   //-s or -c
	uint64 m_bw_limit;   //-b
	std::string m_host;    //-c
	int32 m_len;     //-l, length of packet
	int32 m_mss;     //-m(not including ip,udp,ntdproto head, just pay load)
	int32 m_samples; //-n, minimum sample count
	int32 m_port;    //-p
	float m_probeTime; //-t  
	bool m_bUdp;    //-u
	bool m_bRunOnce; //-o
	bool m_bPacketPair; //running PacketPair
};

extern NDTSettings g_settings;

//////////////////////////////////////////////////////////////////////////

template <class T>
int GetMinMax(std::vector<T> &vec, T &min, T &max)
{
	typename std::vector<T>::const_iterator cit;
	int len;
	T smaller_one, larger_one;
	T tmp1, tmp2;

	/*
	* get & validate vector's length
	*/
	len = vec.size();
	if (len <= 0) {
		return (-1);
	}

	/*
	* if len is odd, then init vec[0] as min and max;
	* else (namely len is even), then min is inited
	* in the smaller one of vec[0] and vec[1], while 
	* max is inited in the larger one. 
	*/
	if (len & 0x00000001) {
		cit = vec.begin();
		min = max = *cit++;
	} else {
		cit = vec.begin();
		tmp1 = *cit++;
		tmp2 = *cit++;
		if (tmp1 < tmp2) {
			min = tmp1;
			max = tmp2;
		} else {
			min = tmp2;
			max = tmp1;
		}
	}

	/*
	* traverse reminder elements in vec: select two elements
	* each time, and first of all comparing that two elements,
	* then compare the smaller one with min, if less that
	* min, let min = smaller_one, the same as the larger one.
	* thus totally need 3 compares for 2 elements, in average
	* each element needs 3/2 compare.
	*/
	while (cit != vec.end()) {
		tmp1 = *cit++;
		tmp2 = *cit++;

		if (tmp1 < tmp2) {
			smaller_one = tmp1;
			larger_one = tmp2;
		} else {
			smaller_one = tmp2;
			larger_one = tmp1;
		}

		if (smaller_one < min)
			min = smaller_one;
		if (larger_one > max)
			max = larger_one;
	}

	return (0);
}
